﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using Kyoh.App.Renamer.RenameEventArgs;

namespace Kyoh.App.Renamer
{
    public sealed partial class RenameWorker : RenameEntryCollection, ISupportInitialize
    {
        public event EventHandler<CompletedEventArgs> ListupCompleted;
        public event EventHandler<CompletedEventArgs> RenameCompleted;
        public event EventHandler<CountChangedEventArgs> CountChanged;
        public event EventHandler<WorkFailedEventArgs> WorkFailed;
        private RenameWorkType _renameWorkType;
        private RenameOptionCollection _batch;
        private Timer _timer;
        private long _period = 500;
        private int _count;
        private readonly object _countLocker = new object();

        #region コンストラクタ

        public RenameWorker() : this(null)
        {
            Debug.WriteLine("RenameWorker::ctor");
        }

        public RenameWorker(IContainer container)
        {
            Debug.WriteLine("RenameWorker::ctor");
            if (container != null)
                container.Add(this);
            InitializeComponent();
        }

        public void BeginInit()
        {
        }

        public void EndInit()
        {
            Debug.WriteLine("RenameWorker::EndInit");
            _timer = new Timer(CheckCount, null, _period, _period);

            backgroundWorker.RunWorkerAsync();
        }

        #endregion

        #region Count

        [DefaultValue(500L)]
        public long CountChangedEventPeriod
        {
            get { return _period; }
            set { _period = value; }
        }

        private void CheckCount(object sender)
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);

            try
            {
                lock (_countLocker)
                {
                    if (_count == Count)
                        return;
                    _count = Count;
                }
                var handler = CountChanged;
                if (handler != null)
                    handler.BeginInvoke(this, new CountChangedEventArgs(_count), null, null);
            }
            catch (Exception exc)
            {
                Debug.WriteLine(exc);
            }
            finally
            {
                _timer.Change(_period, _period);
            }
        }

        #endregion

        #region 処理呼び出し

        private enum RenameWorkType
        {
            Listup,
            Rename,
            Undo,
            Batch,
        }

        public new void Listup(RenameOptionCollection batch)
        {
            Debug.WriteLine("listup");
            ThreadPool.QueueUserWorkItem(e => EntryListup(batch));
        }

        private void EntryListup(RenameOptionCollection batch)
        {
            if (!BeginInterruption(true))
                return;

            try
            {
                _batch = batch;
                _renameWorkType = RenameWorkType.Listup;
            }
            finally
            {
                EndInterruption();
            }
        }

        public new void Rename()
        {
            Debug.WriteLine("rename");
            ThreadPool.QueueUserWorkItem(e => EntryRename());
        }

        private void EntryRename()
        {
            if (!BeginInterruption(false))
                return;
            try
            {
                _renameWorkType = RenameWorkType.Rename;
            }
            finally
            {
                EndInterruption();
            }
        }

        public new void Undo()
        {
            ThreadPool.QueueUserWorkItem(e => EntryUndo());
        }

        private void EntryUndo()
        {
            if (!BeginInterruption(false))
                return;
            try
            {
                _renameWorkType = RenameWorkType.Undo;
            }
            finally
            {
                EndInterruption();
            }
        }

        private void Work(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                ResetWork();

                try
                {
                    if (WorkBody())
                        return;
                }
                catch (Exception exc)
                {
                    var handler = WorkFailed;
                    if (handler != null)
                        handler.BeginInvoke(this, new WorkFailedEventArgs(exc), null, null);
                }
            }
        }

        private bool WorkBody()
        {
            EventHandler<CompletedEventArgs> completion = null;
            switch (_renameWorkType)
            {
                case RenameWorkType.Listup:
                    base.Listup(_batch);
                    completion = ListupCompleted;
                    break;
                case RenameWorkType.Rename:
                    base.Rename();
                    base.Listup(_batch);
                    completion = RenameCompleted;
                    break;
                case RenameWorkType.Undo:
                    base.Undo();
                    base.Listup(_batch);
                    completion = RenameCompleted;
                    break;
            }
            if (completion != null)
                completion.BeginInvoke(this, new CompletedEventArgs(SuspendListup), null, null);
            return false;
        }

        #endregion

        #region 割り込み

        private readonly ManualResetEvent _interruptable = new ManualResetEvent(false);
        private readonly AutoResetEvent _interruption = new AutoResetEvent(false);
        private readonly object _interrupting = new object();
        private readonly object _cancelling = new object();

        // 処理をリセットする
        private void ResetWork()
        {
            // 処理の待機状態を通知する
            _interruptable.Set();

            // 割り込みを待機する
            _interruption.WaitOne();
        }

        // リネーム処理を中断する
        public void CancelRename()
        {
            lock (_cancelling)
            {
                // 処理中断フラグをオンにする
                SuspendRename = true;
                Debug.WriteLine("cancelling");
                // 処理の完了を待機する
                _interruptable.WaitOne();
                Debug.WriteLine("cancelled");

                // 処理中断フラグをオフにする
                SuspendRename = false;
            }
        }

        // リストアップ処理への割り込みを開始する
        private bool BeginInterruption(bool suspend)
        {
            if (!Monitor.TryEnter(_interrupting, 0))
                return false;
            Debug.WriteLine("interrupting");
            try
            {
                // リストアップ処理中断フラグをオンにする
                if (suspend)
                    SuspendListup = true;

                // 処理の完了を待機する
                _interruptable.WaitOne();

                // 処理を開始状態にする
                _interruptable.Reset();
            }
            catch
            {
                Monitor.Exit(_interrupting);
                throw;
            }

            return true;
        }

        // リストアップ処理への割り込みを終了する
        private void EndInterruption()
        {
            try
            {
                // リストアップ処理の中断を破棄する
                SuspendListup = false;

                // 割り込みの終了を通知する
                _interruption.Set();
            }
            finally
            {
                Debug.WriteLine("interrupted");
                // 割り込みを終了状態にする
                Monitor.Exit(_interrupting);
            }
        }

        #endregion
    }
}